"""
<name>Genetic Algorithm</name>
<description>Genetic Algorithm</description>
<contact>Leidy Garzon</contact>
<icon>icons/Ga.svg</icon>
<priority>1050</priority>

"""
from Goldenberry.widgets import GeneticAlgorithm, GbCostFunction, GbBaseOptimizer, GbIndividual, SolutionRep, InitPop, Selector, Mutator, Crosser
from Goldenberry.widgets import OWWidget, load_widget_ui, OWGUI, QtCore, QIntValidator, QObject, QFormLayout, pyqtSignal

class GbGAWidget(OWWidget):
    """Widget for evolution based on genetic algorithm"""
    name = 'Genetic Algorithm'
    cand_size = 20
    max_iters = 100
    xover_prob = 0.8
    muta_prob = 0.3
    evol_strategy_index = 0
    evol_strategy = 'Generational'
    var_size = None
    optimizer = None

    def __init__(self, parent=None, signalManager=None):
        OWWidget.__init__(self, parent,signalManager, title='Genetic Algorithm')
        self.optimizer = GeneticAlgorithm()
        self.setup_interfaces()
        self.setup_ui()

    def setup_interfaces(self):
        self.inputs = [("Cost Function", GbCostFunction, self.set_cost_function), ("Solution Rep", SolutionRep, self.set_solution_rep), ("Initial Pop", InitPop, self.set_init_pop), ("Selector", Selector, self.set_selector), ("Crosser", Crosser, self.set_crossover), ("Mutator", Mutator, self.set_mutator)]
        self.outputs = [("Optimizer", GbBaseOptimizer), ("Solution", GbIndividual)]

    def setup_ui(self):
        load_widget_ui(self)
        pop_size_line = OWGUI.lineEdit(self, self, "cand_size", label="# Candidates", valueType = int, validator = QIntValidator(2,1000000, self.controlArea))        
        max_iters_line = OWGUI.lineEdit(self, self, "max_iters", label="Max Iter.", valueType = int, validator = QIntValidator(0, 1000000, self.controlArea))
        self.varEditor = OWGUI.lineEdit(self, self, "var_size", label="Variables", valueType = int)
        xover_prob_line = OWGUI.doubleSpin(self, self, "xover_prob", 0.0, 1.0, 0.000001,
                                      tooltip="Specify the crossover probability",
                                      label="Crossover Probability"
                                      )
        #xover_prob_line = OWGUI.lineEdit(self, self, "xover_prob", label="Crossover Probability", valueType = float, validator = QIntValidator(0, 1000, self.controlArea))
        muta_prob_line = OWGUI.doubleSpin(self, self, "muta_prob", 0.0, 1.0, 0.000001,
                                      tooltip="Specify the mutation probability",
                                      label="Mutation Probability"
                                      )
        #muta_prob_line = OWGUI.lineEdit(self, self, "muta_prob", label="Mutation Probability", valueType = float, validator = QDoubleValidator(0, 1, self.controlArea))
        evolution_strategy_rbtn = OWGUI.radioButtonsInBox(self, self, value="evol_strategy_index", box='Evolution strategy', btnLabels=["Generational", "SteadyState"], callback=self.evolution_strategy_selected)
        self.paramBox.setLayout(QFormLayout(self.paramBox))
        self.paramBox.layout().addRow(pop_size_line.box, pop_size_line)      
        self.paramBox.layout().addRow(max_iters_line.box, max_iters_line)  
        self.paramBox.layout().addRow(self.varEditor.box, self.varEditor)
        self.paramBox.layout().addRow(xover_prob_line)  
        self.paramBox.layout().addRow(muta_prob_line)  
        self.paramBox.layout().addRow(evolution_strategy_rbtn)  
        # Subscribe to signals
        QObject.connect(self.applyButton,QtCore.SIGNAL("clicked()"), self.apply)
        QObject.connect(self.runButton,QtCore.SIGNAL("clicked()"), self.run)
        QObject.connect(self.stopButton,QtCore.SIGNAL("clicked()"), self.stop)
        self.runButton.setEnabled(False)        
        self.stopButton.setEnabled(False)
        self.varEditor.setEnabled(False)  
    
    def set_solution_rep(self, solution_rep):
         self.optimizer.set_solution_rep(None)
         if None is not solution_rep:
            self.optimizer.set_solution_rep(solution_rep)
            self.__set_run_enable(self.__get_optimizer_ready())

    def set_init_pop(self, init_pop):
        self.optimizer.set_init_pop(None)
        if None is not init_pop:
            self.optimizer.set_init_pop(init_pop)
            self.__set_run_enable(self.__get_optimizer_ready())

    def set_selector(self, selector):
        self.optimizer.set_selector(None)
        if None is not selector:
            self.optimizer.set_selector(selector)
            self.__set_run_enable(self.__get_optimizer_ready())

    def set_mutator(self, mutator):
        self.optimizer.set_mutator(None)
        if None is not mutator:
            self.optimizer.set_mutator(mutator)
            self.__set_run_enable(self.__get_optimizer_ready())

    def set_crossover(self, crosser):
        self.optimizer.set_crosser(None)
        if None is not crosser:
            self.optimizer.set_crosser(crosser)
            self.__set_run_enable(self.__get_optimizer_ready())

    def set_cost_function(self, cost_func):
        self.optimizer.cost_func = None
        if None is not cost_func:
            self.optimizer.cost_func = cost_func(None)
            self.__set_run_enable(self.__get_optimizer_ready())
            self.__set_vareditor_text(str(self.__get_optimizer_var_size()))

    def evolution_strategy_selected(self):
        if self.evol_strategy_index == 1:
            self.evol_strategy = 'SteadyState' 
        else:
            self.evol_strategy = 'Generational'

    def __setup_optimizer(self):
        self.optimizer.setup(cand_size = self.cand_size, max_iters = self.max_iters, crossover_method = self.optimizer.get_crosser().get_crossover_method(), crossover_prob = self.xover_prob, mutation_method = self.optimizer.get_mutator().get_mutation_method(), mutation_prob = self.muta_prob, selection_method = self.optimizer.get_selector().get_selection_method(), evol_strategy = self.evol_strategy)
                
    def apply(self):
        self.__setup_optimizer()    
        self.send("Optimizer" , (self.optimizer, self.name))
        self.__set_run_enable(self.__get_optimizer_ready())
        self.__set_vareditor_text(str(self.__get_optimizer_var_size()))
        
    def run(self):
        self.progressBarInit()
        self.progressBarSet(0)
        self.__set_resultTextEdit_text(text = "") 
        if self.__get_optimizer_ready():
            self.__set_run_enable(False)
            self.__set_stop_enable(True)
            self.__set_apply_enable(False)
            self.__set_vareditor_text(str(self.__get_optimizer_var_size()))
            #self.optimizer.search()
            search = Search(self.optimizer)
            self.connect(search, QtCore.SIGNAL('progress(PyQt_PyObject)'), self.search_progress)
            search.start()

    def __get_optimizer_ready(self):
        return self.optimizer.ready()

    def __get_optimizer_var_size(self):
        return self.optimizer.var_size
        
    def __set_resultTextEdit_text(self, text):
        self.resultTextEdit.setText(text)

    def __set_run_enable(self, enable):
        self.runButton.setEnabled(enable)

    def __set_stop_enable(self, enable):
        self.stopButton.setEnabled(enable)

    def __set_apply_enable(self, enable):
        self.applyButton.setEnabled(enable)

    def __set_vareditor_text(self, text):
        self.varEditor.setText(text)

    def stop(self):
        if self.optimizer is None:
            return
        self.optimizer.stop = True

    def search_progress(self, progress_args):
        self.resultTextEdit.setText(progress_args.text)
        self.progressBarSet(int(progress_args.progress * 100))
        
        if progress_args.progress == 1.0:
            self.runButton.setEnabled(True)
            self.stopButton.setEnabled(False)
            self.applyButton.setEnabled(True)       
            self.progressBarFinished() 
            self.send("Solution", progress_args.result)

    if __name__=="__main__":
        test_widget()

    def test_widget():
        app = QApplication(sys.argv)
        w = GbGeneticAlgorithmWidget()
        w.show()
        app.exec_()

class Search(QtCore.QThread, QObject):#HiloGa
    
    def __init__(self, optimizer):    
        QtCore.QThread.__init__(self)       
        self.optimizer = optimizer
        self.optimizer.callback_func = self.current_progress

    progress = pyqtSignal(object)
     
    def run(self):
        self.__optimizer_search()       

    def current_progress(self, result, progress):
        evals, argmin, argmax, min, max, mean, stdev = statistics  = self.__optimizer_get_statistics()
        text = "Best: %s\ncost:%s\nevals:%s\nargmin:%s\nargmax:%s\nmin val:%s\nmax val:%s\nmean:%s\nstdev:%s"%(result.params, result.cost, evals, argmin, argmax, min, max, mean, stdev)
        self.__set_progress(result, statistics, text, progress)

    def __optimizer_search(self):
        self.optimizer.search()

    def __optimizer_get_statistics(self):
        return self.optimizer.cost_func.statistics()
    
    def __set_progress(self, result, statistics, text, progress):
        self.progress.emit(ProgressArgs(result, statistics, None, text, progress))           

class ProgressArgs:
    
    def __init__(self, result, statistics, distr, text, progress):
        self.statistics = statistics
        self.result = result
        self.distribution = distr
        self.text = text
        self.progress = progress


